//
//  Directory.cpp
//  Simplels
//
//  Created by 陈新唯 on 16/4/16.
//  Copyright © 2016年 陈新唯. All rights reserved.
//

#include "Directory.hpp"
#include <iostream>
#include <fstream>
#include <utility>
#include <unistd.h>

namespace File{
    static DIR *OpenDir_imp(std::string &&File) {
		DIR *dir = nullptr;
		
		if (!(dir = opendir(File.c_str())))
			Unix::throwerrno<DirectoryError>("opendir error");
		
		return dir;
	}
	static DIR *OpenDir(const std::string &s) {
		return OpenDir_imp(path(std::string(s)));
	}
	static DIR *OpenDir(std::string &&s) {
		return OpenDir_imp(path(std::move(s)));
	}
}

std::string File::getCWD() {
	char *buf = nullptr;
	if (!(buf = getcwd(nullptr, -1)))
		Unix::throwerrno<DirectoryError>("getCWD error");
	
	std::string cwd{buf};
	free(buf);
	return cwd;
}

std::string File::getHomeDir() {
	// save home File at a temp file--/tmp/homedirtmpfile
	system(
		   "cd ~;"
		   "pwd > /tmp/homedirtmpfile");
	
	std::ifstream fs("/tmp/homedirtmpfile");
	
	// unlink the temp file
	unlink("/tmp/homedirtmpfile");
	
	// get home File name
	std::string homeDir;
	fs >> homeDir;
	
	return {homeDir};
}

namespace File {
	// class implementation
	// constructor
	Directory::Directory(const std::string &s): dir{OpenDir(s)} {
#ifndef DIRECTORY_NOCOPY
		initCopies();
#endif
	}
	Directory::Directory(std::string &&s): dir{OpenDir(std::move(s))} {
#ifndef DIRECTORY_NOCOPY
		initCopies();
#endif
	}
#ifndef DIRECTORY_NOCOPY
	// copy operations
	Directory::Directory(const Directory &rhs): dir{rhs.dir}, copies{rhs.copies} {
		++*copies;
}
	Directory& Directory::operator=(const File::Directory &rhs) {
		if (this != &rhs) {
			destroy();
			dir = rhs.dir;
			copies = rhs.copies;
			++*copies;
		}
		return *this;
	}
	
	// move operations
	Directory::Directory(Directory &&rhs): dir{rhs.dir}, copies{rhs.copies} {
		rhs.dir = nullptr;
		rhs.copies = nullptr;
	}
	Directory& Directory::operator=(File::Directory &&rhs) {
		if (this != &rhs) {
			destroy();
			dir = rhs.dir;
			copies = rhs.copies;
			rhs.dir = nullptr;
			rhs.copies = nullptr;
		}
		return *this;
	}
#endif

	File::Directory::~Directory() {
		destroy();
	}

	void File::Directory::setDirectory(const std::string &s) {
		destroy();
		dir = OpenDir(s);
#ifndef DIRECTORY_NOCOPY
		initCopies();
#endif
	}
	void File::Directory::setDirectory(std::string &&s) {
		destroy();
		dir = OpenDir(s);
#ifndef DIRECTORY_NOCOPY
		initCopies();
#endif
	}

	/*
	dirent *File::Directory::getEntry() {
		return readdir(dir);
	}
	*/

	void File::Directory::listFiles() const {
		constexpr int filesPerLine = 3;
		
		int i = 0;
		while (auto entry = getEntry()) {
			std::cout << entry->d_name;
			std::cout << ((++i % filesPerLine)? ' ': '\n');
		}
		if (i % filesPerLine)
			std::cout << std::endl;
	}
#ifndef DIRECTORY_NOCOPY
	void File::Directory::initCopies() {
		try {
			copies = new int(1);
		} catch (std::exception &err) {
			closedir(dir);
			throw DirectoryError("operation new error");
		}
	}
#endif
	
	void File::Directory::destroy() {
		if (dir) {
#ifndef DIRECTORY_NOCOPY
			if (copies && --*copies == 0) {
#endif
				closedir(dir);
#ifndef DIRECTORY_NOCOPY
				delete copies;
			}
#endif
			dir = nullptr;
#ifndef DIRECTORY_NOCOPY
			copies = nullptr;
#endif
		}
	}
}
